﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SPMS.Utility;

namespace SPMS.Commons.Logging
{
    /// <summary>
    /// 通用日志工厂类
    /// </summary>
    /// <remarks></remarks>
    /// <history>
    /// [zanezeng]   2008-9-27 2:28 PM    Created
    /// </history>
    public class LogFactory
    {
        #region ---Constructor

        /// <summary>
        /// 初始化<see cref="LogFactory"/>类的新实例
        /// </summary>
        /// <remarks>私有的构造函数，防止外部创建实例</remarks>
        /// <history>
        /// [zanezeng]   2008-10-9 1:44 PM    Created
        /// </history>
        private LogFactory()
        {
        }

        #endregion

        #region ---Method

        /// <summary>
        /// 获得默认的日志对象
        /// </summary>
        /// <returns>日志对象</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]   9/4/2008 5:16 PM    Created
        /// </history>
        /// <remarks></remarks>
        public static ILog GetLog()
        {
            //转调
            return SafeGetLog( string.Empty );
        }

        /// <summary>
        /// 通过名称获得日志
        /// </summary>
        /// <param name="name">日志的名称</param>
        /// <returns>日志对象</returns>
        /// <remarks>
        /// 如果输入的名称为命名空间格式的名称（名称分段，各段之间使用.分割），那么系统自动选择最
        /// 匹配的日志返回
        /// </remarks>
        /// <history>
        /// [zanezeng]   9/4/2008 5:12 PM    Created
        /// </history>
        /// <remarks></remarks>
        public static ILog GetLog( string name )
        {
            //转调
            return SafeGetLog( name );
        }

        /// <summary>
        /// 通过类型获得满足类型名称层次的日志接口
        /// </summary>
        /// <param name="type">类型</param>
        /// <returns>日志对象</returns>
        /// <remarks>
        /// 系统会将类型转化为类型名称来检索日志
        /// 为了支持将不同命名空间下的日志输出到不同的日志源头，用户可以将日志名称配置为命名空间的名称。
        /// 系统会根据命名空间的名称进行最佳匹配。例如日志的名称分别为GrapeCity.Indus.Common和GrapeCity.Kaikei，
        /// 如果你输入的类型的命名空间为GrapeCity.Indus.Common.Core，那么将自动将名为GrapeCity.Indus.Common
        /// 的日志返回
        /// </remarks>
        /// <history>
        /// [zanezeng]   9/4/2008 5:14 PM    Created
        /// </history>
        /// <remarks></remarks>
        public static ILog GetLog( Type type )
        {
            //判断类型是否为NULL
            if (null == type)
            {
                //如果类型为NULL，则使用空白名称获得日志
                return SafeGetLog( string.Empty );
            }

            //使用类型的名称获得日志
            return SafeGetLog( type.FullName );
        }

        /// <summary>
        /// 通过类型获得满足类型名称层次的日志接口
        /// </summary>
        /// <typeparam name="T">类型参数</typeparam>
        /// <returns>日志接口</returns>
        /// <remarks>
        /// 系统会将类型参数转化为类型名称来检索日志
        /// 为了支持将不同命名空间下的日志输出到不同的日志源头，用户可以将日志名称配置为命名空间的名称。
        /// 系统会根据命名空间的名称进行最佳匹配。例如日志的名称分别为GrapeCity.Indus.Common和GrapeCity.Kaikei，
        /// 如果你输入的类型的命名空间为GrapeCity.Indus.Common.Core，那么将自动将名为GrapeCity.Indus.Common
        /// 的日志返回
        /// </remarks>
        /// <history>
        /// [zanezeng]   9/4/2008 5:14 PM    Created
        /// </history>
        /// <remarks></remarks>
        public static ILog GetLog<T>()
        {
            //使用类型的名称获得日志
            return SafeGetLog( typeof( T ).FullName );
        }

        #endregion

        #region ---Property

        /// <summary>
        /// 获得或者设置日志工厂
        /// </summary>
        /// <value>日志工厂</value>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/9/22 14:02    创建
        /// </history>
        public static ILogFactory Factory
        {
            private get
            {
                //判断日志工厂是否已经创建
                if (null != s_LogFactory)
                {
                    //如果日志工厂已经创建，则直接返回日志工厂
                    return s_LogFactory;
                }

                //同步
                lock (s_SyncRoot)
                {
                    //判断日志工厂是否已经创建
                    if (null != s_LogFactory)
                    {
                        //如果日志工厂已经创建，则直接返回日志工厂
                        return s_LogFactory;
                    }

                    //创建日志工厂
                    s_LogFactory = BuildLogFactory();
                }

                //返回日志工厂
                return s_LogFactory;
            }
            set
            {
                //保存日志工厂
                s_LogFactory = value;
            }
        }

        #endregion

        #region ---Private Method

        /// <summary>
        /// 创建日志工厂
        /// </summary>
        /// <returns>创建的日志工厂</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/9/22 14:05    创建
        /// </history>
        private static ILogFactory BuildLogFactory()
        {
            //获得日志工厂配置
            string factoryTypeName = GetLogFactoryConfig();

            //判断日志工厂配置是否存在
            if (string.IsNullOrEmpty( factoryTypeName ))
            {
                //输出日志
                if (LOG.IsInfoEnabled)
                {
                    LOG.Info( "日志工厂配置不存在，返回默认的日志工厂" );
                }

                //如果日志工厂不存在，则返回默认的日志工厂
                return SimpleLogFactory.Default;
            }

            //用于保存日志工厂类型
            Type factoryType = null;

            //尝试解析日志工厂的对象类型
            try
            {
                //解析日志工厂类型
                factoryType = Type.GetType( factoryTypeName, true, true );
            }
            catch (Exception ex)
            {
                //输出日志
                if (LOG.IsErrorEnabled)
                {
                    LOG.Error( ex, "解析日志工厂类型'{0}'失败，返回默认的日志工厂", factoryTypeName );
                }

                //则返回默认的日志工厂
                return SimpleLogFactory.Default;
            }

            //判断是否实现了日志工厂接口
            if (!ReflectionUtility.IsImplementInterface( factoryType, typeof( ILogFactory ) ))
            {
                //输出日志
                if (LOG.IsErrorEnabled)
                {
                    LOG.Error( "日志工厂类型'{0}'没有实现日志工厂接口ILogFactory,返回默认的日志工厂", factoryType );
                }

                //则返回默认的日志工厂
                return SimpleLogFactory.Default;
            }

            try
            {
                //创建日志工厂实例
                return (ILogFactory)Activator.CreateInstance( factoryType );
            }
            catch (Exception ex)
            {
                //输出日志
                if (LOG.IsErrorEnabled)
                {
                    LOG.Error( ex, "实例化日志工厂类型'{0}'失败,返回默认的日志工厂", factoryType );
                }

                //则返回默认的日志工厂
                return SimpleLogFactory.Default;
            }
        }

        /// <summary>
        /// 获得日志工厂定义
        /// </summary>
        /// <returns>获得的日志工厂定义</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/9/22 14:12    创建
        /// </history>
        private static string GetLogFactoryConfig()
        {
            //用于保存工厂类型名称
            string factoryTypeName = null;

            //则尝试从环境变量中获得日志工厂类型名称
            if (TryGetGetEnvironmentVariable( KEY_COMMONS_LOGGING_CONFIG, EnvironmentVariableTarget.Process, out factoryTypeName ))
            {
                //如果加载成功，则返回获得工厂类型名称
                return factoryTypeName;
            }

            //尝试从配置文件中获得日志工厂定义
            factoryTypeName = System.Configuration.ConfigurationManager.AppSettings[KEY_COMMONS_LOGGING_CONFIG];

            //判断配置是否存在
            if (null != factoryTypeName)
            {
                //输出日志
                if (LOG.IsInfoEnabled)
                {
                    LOG.Info( "从配置文件中加载日志工厂配置'{0}'", factoryTypeName );
                }

                //如果工厂类型名称存在，则直接返回
                return factoryTypeName;
            }

            //则尝试从环境变量中获得日志工厂类型名称
            if (TryGetGetEnvironmentVariable( KEY_COMMONS_LOGGING_CONFIG, EnvironmentVariableTarget.User, out factoryTypeName ))
            {
                //如果加载成功，则返回获得工厂类型名称
                return factoryTypeName;
            }

            //则尝试从环境变量中获得日志工厂类型名称
            if (TryGetGetEnvironmentVariable( KEY_COMMONS_LOGGING_CONFIG, EnvironmentVariableTarget.Machine, out factoryTypeName ))
            {
                //如果加载成功，则返回获得工厂类型名称
                return factoryTypeName;
            }

            //输出日志
            if (LOG.IsInfoEnabled)
            {
                LOG.Info( "没有找到日志工厂配置信息" );
            }

            //返回空字符串
            return string.Empty;
        }

        /// <summary>
        /// 尝试从环境变量中获得给定变量的值
        /// </summary>
        /// <param name="variable">要获得环境变量的变量名称</param>
        /// <param name="target">环境变量的范围</param>
        /// <param name="value">用于返回环境变量的值，如果给定环境变量的值不存在，则返回<c>null</c>引用。</param>
        /// <returns>
        /// 	<c>true</c>：给定名称的环境变量存在<br/>
        /// 	<c>false</c>：给定名称的环境变量不存在
        /// </returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2010/1/22 11:39    创建
        /// </history>
        private static bool TryGetGetEnvironmentVariable( string variable, EnvironmentVariableTarget target, out string value )
        {
            //设置默认值
            value = null;

            //则尝试从环境变量中获得日志工厂定义
            try
            {
                //从进程环境变量中加载日志工厂配置
                value = System.Environment.GetEnvironmentVariable( variable, target );
            }
            catch (Exception ex)
            {
                //输出日志
                if (LOG.IsInfoEnabled)
                {
                    LOG.Info( ex, "从环境变量'{0:G}'中加载变量'{1}'失败", target, variable );
                }
            }

            //判断环境变量是否存在
            if (null != value)
            {
                //输出日志
                if (LOG.IsInfoEnabled)
                {
                    LOG.Info( "从环境变量'{0:G}'中加载变量'{1}'成功，变量值为'{2}'", target, variable, value );
                }
            }

            //返回
            return (null != value);
        }

        /// <summary>
        /// 安全的获得给定名称的日志对象
        /// </summary>
        /// <param name="name">要获得的日志对象的名称</param>
        /// <returns>给定名称的日志对象</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/22 14:43    创建
        /// </history>
        private static ILog SafeGetLog( string name )
        {
            try
            {
                //调用日志工厂获得日志对象
                return LogFactory.Factory.GetLog( name );
            }
            catch (Exception ex)
            {
                //输出日志
                if (LOG.IsErrorEnabled)
                {
                    LOG.Error( ex, "使用日志工厂'{0}'获得名为'{1}'的日志发生错误", LogFactory.Factory, name );
                }

                //使用默认日志工厂获得日志对象
                return SimpleLogFactory.Default.GetLog( name );
            }
        }

        #endregion

        #region ---Field

        /// <summary>
        /// 同步对象
        /// </summary>
        private readonly static object s_SyncRoot = new object();

        /// <summary>
        /// 日志工厂
        /// </summary>
        private static ILogFactory s_LogFactory;

        /// <summary>
        /// 默认的日志对象
        /// </summary>
        private readonly static ILog LOG = SimpleLogFactory.Default.GetLog( "SPMS.Commons.Logging" );

        /// <summary>
        /// 通用日志配置键名称
        /// </summary>
        public const string KEY_COMMONS_LOGGING_CONFIG = "leyser-commons-logging";

        #endregion
    }
}
